
class NodeThing < ContainerThing
  property :title, String
  property :string_id, String
  property :is_root, Boolean
  property :parent_identity, String
  property :order, Integer, :default => 10

  attr_accessor :level
  attr_accessor :parent_node
  attr_accessor :first
  attr_accessor :last
  attr_accessor :tree

  def order
    @order ||= 10
  end

  def recieve e
    f = AdminForm.new
    f.recieve e.params

    self.title = f.dump['title']
    self.string_id = f.dump['string_id']
    self.order = f.dump['order']

    if f.delete_button.pushed?
      destroy
    elsif f.save_button.pushed?
      save
    else
      super
      save
    end
  end

  def title
    @title
  end

  def breadth_first &block
    Tree.instance.breadth_first([self], &block)
  end

  def has_children?
    !!Tree.instance.children_of(self)
  end

  def link e
    Element.new(:a, title, :href => e.link('')+path.reverse.tail.map(&:string_id).join('/'))
  end

  def parent_node= node
    if node
      self.parent_identity = node.identity
      if node.level
        self.level = node.level+1
      end
    else
      self.level = 0
    end
    @parent_node = node
  end

  def parent_node
    @parent_node ||= self.class.get(parent_identity)
  end

  def creator e
    f = AdminForm.new

    f.fill({
      :id => id,
      :title => title,
      :string_id => string_id,
      :order => order
    })

    f.element.tag_name = nil

    path.reverse.map { |n|
      n.title
    }.join(" &raquo; ")+"<hr>"+f.to_s+"<hr/>"+super
  end

  def path node = self, tree = nil
    if node
      [node] + (path Tree.instance.nodes_by_identity(node.parent_identity), tree)
    else
      []
    end
  end

  def respond e, &block
    page = self.page
    e.data['page'] = page
    page.content = render(e)
    page.wrap_render e
    block.call if block
    page.head.append_child Element.new(:title, self.title)
    Rack::Response.new(page.document.to_s, 200, {})
  end

  # find page for current node
  def page
    possible_layotus = PageThing.all(
      :place => (path.map &:identity)
    )

    # exact fit?
    layout = possible_layotus.select { |layout|
      !layout.apply_on_children && layout.place == identity
    }.first

    # or just inherited layout
    layout ||= possible_layotus.select { |l|
      l.apply_on_children
    }.first

    layout || raise("There is no layout for '#{self.title}' (#{self.identity}).")
  end

  def create_default_page
    page = PageThing.create_default
    page.save
    page
  end



  
  def save
    if is_root
      (!NodeThing.first(:is_root => true)) ||
        raise('there is a root already')
    end
    super.tap {
      tree && tree.clear_cache
    }
  end

  class AdminForm < Form

    attr_accessor :delete_button, :save_button

    def initialize
      super
      add_fields
    end

    def add  o
      self << o
    end

    def add_fields
      add InputHidden.new({:name => 'id'})
      add InputHidden.new({:name => 'parent_node_id'})
      add InputText.new({
        :name => 'title',
        :value => 'název'
      })
      add InputText.new({
        :name => 'string_id',
        :value => 'text v url'
      })
      add InputInteger.new({
        :name => 'order',
        :size => '4'
      })
      add @save_button = SubmitButton.new({
          :name => 'save-node',
          :value => 'ulož'
        })
      add @delete_button = SubmitButton.new({
          :name => 'delete-node',
          :value => 'smaž',
          :onclick => 'return confirm("Opravdu smazat?")'
        })
      add '</div>'
    end
  end


  class TestPage < AppTest
    def test_find_exact_layout
      DataMapper.setup(:test, :adapter => 'in_memory')
      repository(:test) {
        root = NodeThing.new(:is_root => true)
        root.save

        page = PageThing.new
        page.place = root.identity

        page.apply_on_children = false
        page.save

        eq root.page, page

        page.apply_on_children = true
        page.save

        eq root.page, page
      }
    end

    def test_find_inherited_layout
      DataMapper.setup(:test, :adapter => 'in_memory')
      repository(:test) {
        root = NodeThing.new(:is_root => true)
        root.save

        child = NodeThing.new()
        child.parent_node = root
        child.save

        page = PageThing.new
        page.apply_on_children = true
        page.place = root.identity
        page.save

        eq child.page, page
      }
    end

    def test_inherited_layout_must_apply_on_children
      DataMapper.setup(:test, :adapter => 'in_memory')
      repository(:test) {
        root = NodeThing.new(:is_root => true)
        root.save

        child = NodeThing.new()
        child.parent_node = root
        child.save

        page = PageThing.new()
        page.apply_on_children = false
        page.place = root.identity
        page.save

        eq child.page, nil
      }
    end

    def test_exact_fit_is_better_than_inherited_layout
      DataMapper.setup(:test, :adapter => 'in_memory')
      repository(:test) {
        root = NodeThing.new(:is_root => true)
        root.save

        child = NodeThing.new()
        child.parent_node = root
        child.save

        inherited = PageThing.new
        inherited.place = root.identity
        inherited.save

        exact = PageThing.new
        exact.place = child.identity
        exact.save

        eq child.page, exact
      }
    end

    def test_exact_fit_is_better_than_exact_which_applies_on_children
      DataMapper.setup(:test, :adapter => 'in_memory')
      repository(:test) {
        root = NodeThing.new(:is_root => true)
        root.save

        general = PageThing.new
        general.place = root.identity
        general.apply_on_children = true
        general.save

        custom = PageThing.new
        custom.place = root.identity
        custom.apply_on_children = false
        custom.save

        eq root.page, custom
      }
    end

  end

end
